// Copyright Epic Games, Inc. All Rights Reserved.

#include "UI/LyraJoystickWidget.h"

#include "CommonHardwareVisibilityBorder.h"
#include "Components/Image.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraJoystickWidget)

#define LOCTEXT_NAMESPACE "LyraJoystick"

/**
 * 构造函数
 * @param ObjectInitializer 对象初始化器
 */
ULyraJoystickWidget::ULyraJoystickWidget(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) // 调用父类构造函数
{
	SetConsumePointerInput(true); // 设置消耗指针输入
}

/**
 * 触摸开始时的原生处理
 * 记录触摸起源并捕获鼠标
 * @param InGeometry 几何信息
 * @param InGestureEvent 手势事件
 * @return 回复
 */
FReply ULyraJoystickWidget::NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
	Super::NativeOnTouchStarted(InGeometry, InGestureEvent); // 调用父类方法
	
	TouchOrigin = InGestureEvent.GetScreenSpacePosition(); // 设置触摸起源为屏幕空间位置

	FReply Reply = FReply::Handled(); // 创建已处理的回复
	if (!HasMouseCaptureByUser(InGestureEvent.GetUserIndex(), InGestureEvent.GetPointerIndex())) // 如果没有通过用户和指针索引捕获鼠标
	{
		Reply.CaptureMouse(GetCachedWidget().ToSharedRef()); // 捕获鼠标
	}
	return Reply; // 返回回复
}

/**
 * 触摸移动时的原生处理
 * 处理触摸增量并捕获鼠标
 * @param InGeometry 几何信息
 * @param InGestureEvent 手势事件
 * @return 回复
 */
FReply ULyraJoystickWidget::NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
	Super::NativeOnTouchMoved(InGeometry, InGestureEvent); // 调用父类方法
	HandleTouchDelta(InGeometry, InGestureEvent); // 处理触摸增量

	FReply Reply = FReply::Handled(); // 创建已处理的回复
	if (!HasMouseCaptureByUser(InGestureEvent.GetUserIndex(), InGestureEvent.GetPointerIndex())) // 如果没有通过用户和指针索引捕获鼠标
	{
		Reply.CaptureMouse(GetCachedWidget().ToSharedRef()); // 捕获鼠标
	}
	return Reply; // 返回回复
}

/**
 * 触摸结束时的原生处理
 * 停止输入模拟并释放鼠标捕获
 * @param InGeometry 几何信息
 * @param InGestureEvent 手势事件
 * @return 回复
 */
FReply ULyraJoystickWidget::NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
	StopInputSimulation(); // 停止输入模拟
	return FReply::Handled().ReleaseMouseCapture(); // 返回已处理的回复并释放鼠标捕获
}

/**
 * 鼠标离开时的原生处理
 * 停止输入模拟
 * @param InMouseEvent 鼠标事件
 */
void ULyraJoystickWidget::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
	Super::NativeOnMouseLeave(InMouseEvent); // 调用父类方法
	StopInputSimulation(); // 停止输入模拟
}

/**
 * 原生Tick函数
 * 更新摇杆前景位置并输入键值
 * @param MyGeometry 几何信息
 * @param InDeltaTime 增量时间
 */
void ULyraJoystickWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime); // 调用父类方法

	if (!CommonVisibilityBorder || CommonVisibilityBorder->IsVisible()) // 如果通用可见性边框不存在或可见
	{
		// 用向量移动内部摇杆图标
		if (JoystickForeground && JoystickBackground) // 如果摇杆前景和背景都存在
		{
			JoystickForeground->SetRenderTranslation( // 设置渲染平移
				(bNegateYAxis ? FVector2D(1.0f, -1.0f) : FVector2D(1.0f)) * // 根据是否否定Y轴应用变换
				StickVector * // 摇杆向量
				(JoystickBackground->GetDesiredSize() * 0.5f) // 背景期望大小的一半
			);
		}
		InputKeyValue2D(StickVector); // 输入2D键值
	}
}

/**
 * 处理触摸增量
 * 计算摇杆向量
 * @param InGeometry 几何信息
 * @param InGestureEvent 手势事件
 */
void ULyraJoystickWidget::HandleTouchDelta(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{
	const FVector2D& ScreenSpacePos = InGestureEvent.GetScreenSpacePosition(); // 获取屏幕空间位置
	
	// 几何的本地中心就是它的大小
	FVector2D LocalStickCenter = InGeometry.GetAbsoluteSize(); // 获取绝对大小作为本地摇杆中心

	FVector2D ScreenSpaceStickCenter = InGeometry.LocalToAbsolute(LocalStickCenter); // 本地到绝对坐标转换
	// 获取从原点的偏移
	FVector2D MoveStickOffset = (ScreenSpacePos - ScreenSpaceStickCenter); // 计算移动摇杆偏移
	if (bNegateYAxis) // 如果否定Y轴
	{
		MoveStickOffset *= FVector2D(1.0f, -1.0f); // 应用Y轴否定
	}
	
	FVector2D MoveStickDir = FVector2D::ZeroVector; // 移动摇杆方向
	float MoveStickLength = 0.0f; // 移动摇杆长度
	MoveStickOffset.ToDirectionAndLength(MoveStickDir, MoveStickLength); // 转换为方向和长度

	MoveStickLength = FMath::Min(MoveStickLength, StickRange); // 钳制长度在摇杆范围内
	MoveStickOffset = MoveStickDir * MoveStickLength; // 重新计算偏移

	StickVector = MoveStickOffset / StickRange; // 计算摇杆向量（归一化）
}

/**
 * 停止输入模拟
 * 重置触摸起源和摇杆向量
 */
void ULyraJoystickWidget::StopInputSimulation()
{
	TouchOrigin = FVector2D::ZeroVector; // 重置触摸起源
	StickVector = FVector2D::ZeroVector; // 重置摇杆向量
}

#undef LOCTEXT_NAMESPACE // 取消定义本地化文本命名空间